甚麼時候用類別點(.),甚麼是後用變數點(.)?點出來的東西怎麼都不一樣?這個問題曾經困擾了筆者很久。如下案例:
String.IsNullOrEmpty("ITHome_Ironman_2017");
String demo = "ITHome_Ironman_2017";
demo = demo.ToUpper();
後來筆者才搞懂String.IsNullOrEmpty()
是靜態方法static function。同樣的案例發身在C/C++、JAVA身上都有。筆者身邊有一些朋友一直搞不懂這是甚麼意思,本篇就此問題來做討論。
事實上,所有的程式被組譯後都可以得到程式/物件的大小,當OS啟動該程式的時候,就會將程式掛載在記憶體中,並且賦予一個記憶體位置,並跳躍至該程式的啟動區域執行。而當程式中出現new
的時候,會跟記憶體申請記憶體空間,當記憶體擁有對應的大小的記憶時就會將其提供給該程式使用,而該程式也能針對該區域的記憶體進行讀寫。
而static變數就是在載入程式後會主動配給記憶體給程式(僅一次),後續無論實例化多少次,記憶體位置都一樣。
其實,函式在最後組譯後其實是一個記憶體位置(32/64bits),藉由這個特性靜態方法也有同樣的效果。
使用static
關鍵字可以讓程式被OS載入時就被儲存於記憶體中,直到Application結束為止。如下:
private static float PI = 0.0f; //靜態變數
private float radius = 0.0f; //一般變數
值得特別說明的是,如果您將一個靜態變數定義在一個物件中,您可以利用靜態建構函式來初始化靜態變數。
注意:靜態建構函式只會執行一次
實務上,通常會將公共變數全部設定為靜態變數,例如上述物件中含有一個PI的定義。這樣可以共享記憶體,相對較節省記憶體。但事情總是有兩面性的,也因為共享記憶體,所以要特別注意存取權限以及存取時機,例如:通常不會在建構式中設定靜態變數,以免造成其他程式誤取。
為了證明靜態建構式只會存取一次,筆者撰寫測試代碼如下。
//filename : Circle.cs
public class Circle
{
private static float PI = 0.0f;
private float radius = 0.0f;
static Circle()
{
Console.WriteLine("靜態建構函式-啟動");
PI = 3.141592653f;
}
public Circle(float radius)
{
Console.WriteLine("建構函式-啟動");
this.radius = radius;
}
public float getArea() { return PI * this.radius * this.radius; }
}
//filename : Program.cs
class Program
{
static void Main(string[] args)
{
Circle circle1 = new Circle(3.0f);
Circle circle2 = new Circle(5.0f);
Console.WriteLine(circle1.getArea());
Console.WriteLine(circle2.getArea());
Console.ReadKey();
}
}
如上,雖然我們new了兩次變數,但是靜態建構式只有執行一次。再次驗證靜態變數會共享資料。
事實上,如果您擅長使用C/C++,您可以直接利用sizeof()
來驗證物件是否共用記憶體。有關證明方式有機會筆者改天再行討論。
這篇文章應該是 C# 的,我這邊補充 JAVA 的
java class 裡的 constructor 似乎不能以 static 修飾之
會有如下的錯誤訊息:
.\Circle.java:6: error: modifier static not allowed here
static Circle()
^
1 error
以我的理解是,受到 static 修飾的 class 成員,只屬於那個 class,不會被繼承。
所以像是 constructor 這樣會被 subclass object 呼叫的成員,無法用 static 修飾之。
references:
https://beginnersbook.com/2013/05/static-constructor/
https://stackoverflow.com/questions/10291949/are-static-methods-inherited-in-java
感謝您的補充~